package Server;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import LinkDatabase.IUser;
import LinkDatabase.IUserImp;
import LinkDatabase.User;
import opt.Msg;
import opt.Opertor;

public class MyServer {
	private Msg commondList = new Msg();// 指令操作数组
	private Selector selector;// 选择器
	private ByteBuffer buffer = ByteBuffer.allocate(2048);// 缓冲区
	private ByteBuffer readBuffer=ByteBuffer.allocate(1024);//读缓冲区
	private Map<SocketChannel, String> socketChannelList = new HashMap<>();
	private Map<String, Boolean> clientMap = new HashMap<>();// 记录客户端信息
	private ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(1);// 定时器广播
	private String portString;
	private int tik;// 帧同步时间
	private int playernum = 0;// 玩家人数
	private int maxplayer;
	private Boolean isRun = false;
	private int fRame = 0;
	private List historyList = new ArrayList<Msg>();
	private Random random;
	/**
	 * 
	 * 创建服务端通道.
	 * @author 陈俊宏
	 * 
	 * @param port
	 * 
	 * @throws IOException
	 * 
	 */
	public MyServer() throws IOException {
		// 读取文件配置
		Properties properties = new Properties();
		InputStream iStream = new FileInputStream("Server.properties");
		properties.load(iStream);
		portString = properties.getProperty("port");
		tik = Integer.parseInt(properties.getProperty("tik"));
		maxplayer = Integer.parseInt(properties.getProperty("maxplayer"));
		iStream.close();
		// 创建通道
		ServerSocketChannel sschannel = null;
		sschannel = ServerSocketChannel.open();
		// 绑定端口
		sschannel.bind(new InetSocketAddress(Integer.parseInt(portString)));
		// 设置非阻塞模式
		sschannel.configureBlocking(false);
		// 获取selector
		selector = Selector.open();
		// 注册通道到selector
		sschannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("server is running at" + sschannel.getLocalAddress());
	}

	/**
	 * 
	 * 循环不断的监听客户端的链接.
	 * 
	 * @throws IOException
	 * 
	 */
	public void listen() throws IOException {
		while (true) {
			// 阻塞，直至有事件就绪
			selector.select();
			// 获取SelectionKey集合，使用迭代器读取SelectionKey
			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				// 读到SelectionKey
				SelectionKey sk = iterator.next();
				// 处理SelectionKey
				handleSelectionKey(sk);
				// 移除已经处理的SelectionKey
				iterator.remove();
			}
			// 处理完毕，清空SelectionKey集合
			selector.selectedKeys().clear();
		}
	}
	/**
	 * 
	 * 处理SelectionKey.
	 * 
	 * @param sk SelectionKey
	 * 
	 */
	private void handleSelectionKey(SelectionKey sk) throws IOException {
		ServerSocketChannel serverSocketChannel;
		SocketChannel socketChannel;
		if (sk.isAcceptable()) {// 可接收
			// 从SelectionKey中获取 ServerSocketChannel通道
			serverSocketChannel = (ServerSocketChannel) sk.channel();
			// 接受来自客户端的连接
			socketChannel = serverSocketChannel.accept();
			// 设置新连接的通道为非阻塞
			socketChannel.configureBlocking(false);
			// 将该通道注册到selector上,并设置对读事件感兴趣
			socketChannel.register(selector, SelectionKey.OP_READ);
			System.out.println("[" + socketChannel.getRemoteAddress() + "] is already connected to the server!");
		} else if (sk.isReadable()) {// 读就绪
			// 获取通道
			socketChannel = (SocketChannel) sk.channel();
			// 接收消息
			Msg msg = readMsg(socketChannel);
			handeMsg(msg, socketChannel);
		}
	}

	/**
	 * 处理消息.
	 * 
	 * @throws IOException
	 */
	private void handeMsg(Msg msg, SocketChannel socketChannel) throws IOException {
		if(msg==null)return;
		if (msg.getFrame() < 0) {
			String[] ssString = msg.getList().get(0).split(" ");
			if (ssString[0].equals("V_LOGIN")) {
				String userString = ssString[1];
				String passString = ssString[2];
				// 调用登录的方法
				int rlt=-1;
				IUser isIUser =new IUserImp();
				rlt =isIUser.queryUser(userString, passString);
				if (rlt > 0) {
					if (!isRun) {
						// 登录成功
						Msg msg2 = new Msg(-1, "LOGINED");
						writeMsg(msg2, socketChannel);
					}
				} else {
					Msg msg2 = new Msg(-1, "LOGINEDFAIL");
					writeMsg(msg2, socketChannel);
				}
			} else if (ssString[0].equals("OK")) {
				clientMap.put(socketChannel.getRemoteAddress().toString(), true);
				if (checkprepared()) {
					gamebegin();
				}
			} else if(ssString[0].equals("MATCH")){
				// 将玩家加入队列
				socketChannelList.put(socketChannel, ssString[1]);
				clientMap.put(socketChannel.getRemoteAddress().toString(), false);
				playernum++;
				// 检查人数是否符号
				checknum();
			}else if (ssString[0].equals("V_register")) {
				IUser isIUser =new IUserImp();
				int rlt =-1;
				rlt=isIUser.addUser(new User(ssString[1],ssString[2],ssString[3],ssString[4]));
				if(rlt>0) {
					Msg msg2=new Msg(-1,"registered");
					writeMsg(msg2, socketChannel);
				}else {
					Msg msg2=new Msg(-1,"registerFAIL");
					writeMsg(msg2, socketChannel);
				}
			}else if(ssString[0].equals("allkill")) {
				clientMap.put(socketChannel.getRemoteAddress().toString(), true);
				System.out.println(clientMap);
				if(checkkill()) {
					Msg msg2 =new Msg(-1,"bMonster "+random.nextInt(1024));
					broadcastMsg2(msg2);
					for(Map.Entry<String, Boolean>m:clientMap.entrySet()) {
						clientMap.put(m.getKey(),false);
					}

				}
			}else if(ssString[0].equals("Query")) {
				IUser is=new IUserImp();
				List<User> list=is.queryAll();
				String questionString=null;
				String answtringString=null;
				for(User u:list) {
					if(u.getUserName().equals(ssString[1])) {
						questionString=u.getUserFindQuestion();
						answtringString=u.getUserFindAnswer();
					}
				}
				Msg msg2=new Msg(-1, "UserQuestion "+questionString+" "+answtringString);
				writeMsg(msg2, socketChannel);
			}else if(ssString[0].equals("update")) {
				IUser is=new IUserImp();
					User user=new User();
					user.setUserName(ssString[1]);
					user.setUserPassword(ssString[2]);
					User user2=new User();
					user2.setUserName(ssString[1]);
					int rlt =is.updateUser(user2, user);
					Msg msg2=null;
					if(rlt>0) {
						 msg2=new Msg(-1,"update_success");
					}else {
						 msg2=new Msg(-1,"update_fail");
					}
					writeMsg(msg2, socketChannel);
			}
		}
		else {
			//服务器转发
			commondList.getList().addAll(msg.getList());
		}
	}
	/**
	 * 检查是否全部准备好.
	 * 
	 * 
	 */
	public boolean checkprepared() {
		boolean t = false;
		for (boolean k : clientMap.values()) {
			if (!k)
				return t;
		}
		return true;
	}

	/**
	 * 检查是否已经达到人数.
	 * 
	 * @throws IOException
	 */
	private void checknum() throws IOException {
		if (playernum >= maxplayer) {
			try {
			random=new Random();
			Msg msg = new Msg(-1, "V_BeginGame "+random.nextInt(1024));
			// 向玩家广播可以开始了。
			broadcastMsg2(msg);
			isRun = true;
			}catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
	/**
	 * 检查是否杀完.
	 */
	private boolean checkkill() {
		for(boolean b:clientMap.values()) {
			if(!b)return false;
		}
		return true;
	}
	/**
	 * 
	 * 接收消息
	 * 
	 * @param socketChannel
	 * 
	 * @throws IOException
	 * 
	 * @return 消息
	 * 
	 */
	private Msg readMsg(SocketChannel socketChannel) throws IOException {
		try {
			// 初始化缓冲区
			readBuffer.clear();
			Msg msg = null;
			while ((socketChannel.read(readBuffer)) > 0) {
				readBuffer.flip();
				int len = readBuffer.limit() - readBuffer.position();
				byte[] bytes1 = new byte[len];
				readBuffer.get(bytes1);
				msg = (Msg) Opertor.ByteToObject(bytes1);
			}
			return msg;
		} catch (Exception e) {
			System.out.println("连接异常终止");
			clientMap.remove(socketChannel.getRemoteAddress().toString());
			socketChannelList.remove(socketChannel);
			socketChannel.close();// 关闭通道
			return null;
		}
	}
	/**
	 * 
	 * 发送消息.
	 * 
	 * @param str
	 * 
	 * @param socketChannel
	 * 
	 * @throws IOException
	 * 
	 */
	private void writeMsg(Msg str, SocketChannel socketChannel) throws IOException {
		buffer.clear();
		buffer.put(Opertor.ObjectToByte(str));
		buffer.flip();
		socketChannel.write(buffer);
	}
	
	/**
	 * 
	 * 群发消息，方法一.
	 * 
	 * @param msg 信息载体
	 * 
	 * @throws IOException
	 * 
	 */
	private void broadcastMsg(Msg msg) throws IOException {
		for (SelectionKey key : selector.keys()) {
			Channel target = key.channel();
			if (target.isOpen() && target instanceof SocketChannel) {
				writeMsg(msg, (SocketChannel) target); 
			}
		}
	}

	/**
	 * 群发消息二
	 * 
	 * @param msg 信息载体
	 * 
	 * @throws IOException
	 */
	private void broadcastMsg2(Msg msg) throws IOException {
		for (SocketChannel channel : socketChannelList.keySet()) {
			if (channel.isOpen()) {
				buffer.clear();
				buffer.put(Opertor.ObjectToByte(msg));
				buffer.flip();
				channel.write(buffer);
			}
		}
	}
	/**
	 * 开启线程进行定时广播
	 */
	private void gamebegin() {
		System.out.println("开始游戏");
		timer.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				if (fRame == 0) {
					Msg msg = new Msg();
					msg.setFrame(fRame);
					msg.setList(new ArrayList<String>());
					try {
						for (String name : socketChannelList.values()) {
							msg.getList().add("birth " + name);
						}
						commondList.setList(msg.getList());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				try {
					if(isRun) {
				broadcastMsg2(commondList);
				historyList.add(commondList);
				System.out.println(commondList);
				commondList.getList().clear();
				commondList.setFrame(++fRame);
					}
				}catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		}, tik, tik, TimeUnit.MILLISECONDS);
	}

	/**
	 * 
	 * 
	 * @param args
	 * 
	 * @throws IOException
	 * 
	 */
	public static void main(String[] args) throws IOException {
		MyServer myServer = new MyServer();
		myServer.listen();
	}
}
